﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Policy;
using System.Text;

namespace THenu.WMI.IPConfiguration
{
    /* *************************
 * Cross AppDomain Singleton
 * *************************
 * 
 * The solution we want is that the singleton class is created in a
 * given AppDomain and in all other AppDomains we get a transparent
 * proxy to that singleton. 
 * 
 * In order to do this we need to have the ability to enumerate
 * through existing AppDomains in order to create an instance in the
 * correct AppDomain (at least I found this to be cool way of doing it).
 * I came across a thread on microsoft.public.dotnet.framework.clr 
 * that gave me a good solution 
 * (http://groups.google.com/group/microsoft.public.dotnet.framework.clr/browse_frm/thread/dba9c445ad8d5c3/9df14bf0af393c28?lnk=st&q=enumerate+appdomain+group%3Amicrosoft.public.dot%20net.*&rnum=5#9df14bf0af393c28)
 * 
 * You need to add a reference to the MSCOREE.TLB which is situated 
 * in the .net directory (c:\windows\microsoft.net\framework\v2.0.50727,
 * depends on your runtime version). When you add a reference to it 
 * you'll get an Interop.mscoree.dll added to your output directory.
 * You will have to have this alongside your deployment if you're going
 * to use this in a solution.
 */
    /// <summary>
    /// Cross AppDomain Singleton
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SingletonDomain<T> : MarshalByRefObject where T : new()
    {
        private static readonly string AppDomainName = "SingletonAppDomain";
        private static T _instance;

        private static AppDomain GetAppDomain(string friendlyName)
        {
            IntPtr enumHandle = IntPtr.Zero;
            mscoree.CorRuntimeHost host = new mscoree.CorRuntimeHost();
            try
            {
                host.EnumDomains(out enumHandle);
                object domain = null;
                do
                {
                    host.NextDomain(enumHandle, out domain);
                    if (domain != null && domain is AppDomain)
                    {
                        AppDomain appDomain = (AppDomain)domain;
                        if (appDomain.FriendlyName.Equals(friendlyName))
                        {
                            return appDomain;
                        }
                    }
                } while (domain != null);
                return null;
            }
            finally
            {
                host.CloseEnum(enumHandle);
                Marshal.ReleaseComObject(host);
                host = null;
            }
        }

        public static T Instance
        {
            get
            {
                if (null == _instance)
                {
                    AppDomain appDomain = GetAppDomain(AppDomainName);
                    if (null == appDomain)
                    {
                        AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
                        setup.ApplicationName = "SingletonAppDomain";

                        // Set up the Evidence
                        Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);

                        appDomain = AppDomain.CreateDomain(AppDomainName, evidence, setup);
                    }
                    Type type = typeof(T);
                    T instance = (T)appDomain.GetData(type.FullName);
                    if (null == instance)
                    {
                        instance = (T)appDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
                        appDomain.SetData(type.FullName, instance);
                    }
                    _instance = instance;
                }
                return _instance;
            }
        }
    }
}
